#ifndef _TRANSFORMATION_
#define _TRANSFORMATION_

#include "vl/VLd.h"
#include "Dof.h"
#include <vector>

#ifdef WIN32
#include <windows.h>
#endif
#ifdef MACOSX
#include <Fl/glut.H>
#else
//#include <GL/glut.h>
#endif

#include <GL/gl.h>
#include <GL/glu.h>

#ifndef M_PI
#define M_PI 3.141592653589793
#endif

#define TRANSFORM_MAGIC_NO 2500

class Joint;
class Dof;

using namespace std;

class Transformation{
public:
	enum TransFormType { T_ROTATEX, T_ROTATEY, T_ROTATEZ, T_ROTATEEXPMAP, T_ROTATEQUAT, T_TRANSLATE, T_TRANSLATEX, T_TRANSLATEY, T_TRANSLATEZ };

protected:
	vector<Dof *> mDofs;	// collection of Dofs
	char mName[64];
	TransFormType mType;
	int mID;	// unique id
	int mModelID;	// position in the model transform vector

	Joint *mJoint;	// Transformation associated with
	bool mVariable;	// true when it is a variable and included int he model
	Mat4d *mTransform;	// transformation matrix will be stored here
	Mat4d *mInvTransform;	// inverse transformation matrix will be stored here
	Mat4d *mDeriv;	// derivative of transformation matrix will be stored here
	static int mTransformCount;
public:
	Transformation();
	~Transformation(){
		delete mTransform;
		delete mDeriv;
		for(int i=0; i<mDofs.size(); i++){
			delete mDofs[i];
		}
		mDofs.clear();
	}

	inline TransFormType getType(){return mType;}
	inline int getID(){return mID;}
	inline int getModelID(){return mModelID;}
	inline void setModelID(int _id){mModelID=_id;}
	bool isPresent(Dof *d);	// true if d is present in the dof list
	inline Joint* getJoint(){return mJoint;}
	inline void setJoint(Joint *_joint){mJoint=_joint;}
	inline int getNumDofs(){return static_cast<int>(mDofs.size());}
	inline Dof* getDof(int i){return mDofs[i];}
	bool getVariable(){return mVariable;}
	void setVariable(bool _var){mVariable = _var;}
	
	virtual void applyGLTransform()=0;	// apply transform in GL
	// used for fast application of matrices (without actual multiplication) wherever possible e.g. in case of translation
	virtual void applyTransformMat(Vec4d *v);
	virtual void applyTransformMat(Mat4d *m);
	virtual void applyInvTransformMat(Mat4d *m);
	virtual void applyDerivativeMat(Dof *d, Vec4d *v);
	virtual void applyDerivativeMat(Dof *d, Mat4d *m);
	virtual void applyDerivativeMat(Dof *d1, Dof *d2, Vec4d *v);
	virtual void applyDerivativeMat(Dof *d1, Dof *d2, Mat4d *v);

protected:
	virtual Mat4d *getTransformMat()=0;	// computes and stores in above
	virtual Mat4d *getInvTransformMat(){
		(*mInvTransform) = inv(*(getTransformMat()));
		return mInvTransform;
	}
	virtual Mat4d *getDerivativeMat(Dof *d)=0;	// get derivative wrt to a dof
	virtual Mat4d *getDerivativeMat(Dof *d1, Dof *d2)=0;	// get derivative wrt to 2 dofs present in a transformation
};

#endif
